/*
	assignment 1, comp 6461
*/

char* getmessage(char *);

#pragma comment( linker, "/defaultlib:ws2_32.lib" )
#include <winsock2.h>
#include <ws2tcpip.h>
#include <winsock.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <windows.h>
#include <fstream>
#include <algorithm>
#include <string>
#include <sstream>
#include <Lmcons.h>

using namespace std;

//user defined port number
#define REQUEST_PORT 0x7070;
int port = REQUEST_PORT;

//socket data types
SOCKET s;
SOCKADDR_IN sa;         // filled by bind
SOCKADDR_IN sa_in;      // fill with server info, IP, port

#define BUFFER_SIZE 128
//buffer data types
char szbuffer[BUFFER_SIZE];

char *buffer;

int ibufferlen = 0;

int ibytessent;
int ibytesrecv = 0;



//host data types
HOSTENT *hp;
HOSTENT *rp;

char localhost[11], remotehost[11];


//other

HANDLE test;

DWORD dwtest;

/*
	convert string to int
*/
int stringToInt(string s)
{
	istringstream stringBuffer(s);
	int value;
	stringBuffer >> value;
	return value;
}

char * sendData(char words[])
{
	sprintf_s(szbuffer, words);

	ibytessent = 0;
	ibufferlen = strlen(szbuffer);
	ibytessent = send(s, szbuffer, ibufferlen, 0);
	if (ibytessent == SOCKET_ERROR)
		throw "Send failed\n";
	/*
	else
		std::cout << "Message to server: " << szbuffer <<endl;
	*/

	memset(szbuffer,'\0', BUFFER_SIZE);

	//wait for reception of server response.
	ibytesrecv = 0;
	if ((ibytesrecv = recv(s, szbuffer, BUFFER_SIZE, 0)) == SOCKET_ERROR)
		throw "Receive failed\n";
	/*
	else
		std::cout << "Successful message replied from server: " << szbuffer;
	*/
	return szbuffer;
}

int getFileSize(const string &fileName)
{
	ifstream file(fileName, ifstream::binary);
	file.seekg(0, file.end);
	int size = file.tellg();
	file.close();
	return size;
}

int getTextFileCount(const string &fileName)
{
	ifstream file(fileName);
	int count = 0;
	while (true)
	{
		int position = 0;
		while (!file.eof())
		{
			char c = file.get();
			if (c != EOF)
			{
				position++;
				if (position == BUFFER_SIZE - 1)
				{
					break;
				}
			}
		}
		count++;
		if (file.eof())
		{
			break;
		}
	}
	file.close();
	return count;
}



string getSystemUserName()
{
	char username[UNLEN + 1];
	DWORD username_len = UNLEN + 1;
	GetUserName(username, &username_len);
	//std::cout << username;
	string strUserName(username);
	return strUserName;
}



bool fileExist(const string &fileName)
{
	ifstream file(fileName);
	if (file.good())
	{
		file.close();
		return true;
	}
	else
	{
		file.close();
		return false;
	}
}

bool isBinaryFile(const string &fileName)
{
	int c;
	ifstream file(fileName);
	while ((c = file.get()) != EOF && c <= 127)
		;
	if (c == EOF)
	{
		file.close();
		return false;
	}
	else
	{
		file.close();
		return true;
	}
}


int main(void){

	WSADATA wsadata;
	try {
		if (WSAStartup(0x0202, &wsadata) != 0)
		{
			std::cout << "Error in starting WSAStartup()" << endl;
		}
		else 
		{
			buffer = "WSAStartup was successful\n";
			WriteFile(test, buffer, sizeof(buffer), &dwtest, NULL);

			/* Display the wsadata structure */
			std::cout << endl
				<< "wsadata.wVersion " << wsadata.wVersion << endl
				<< "wsadata.wHighVersion " << wsadata.wHighVersion << endl
				<< "wsadata.szDescription " << wsadata.szDescription << endl
				<< "wsadata.szSystemStatus " << wsadata.szSystemStatus << endl
				<< "wsadata.iMaxSockets " << wsadata.iMaxSockets << endl
				<< "wsadata.iMaxUdpDg " << wsadata.iMaxUdpDg << endl;
		}

		//Display name of local host.
		gethostname(localhost, 10);
		std::cout << "Local host name is \"" << localhost << "\"" << endl;

		if ((hp = gethostbyname(localhost)) == NULL)
			throw "gethostbyname failed\n";

		while (true)
		{
			//Ask for name of remote server
			std::cout << "please enter your remote server name :" << flush;
			cin >> remotehost;
			std::cout << "Remote host name is: \"" << remotehost << "\"" << endl;

			if ((rp = gethostbyname(remotehost)) == NULL)
			{
				cout << "remote gethostbyname failed\n";
				continue;
			}

			//Create the socket
			if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
				throw "Socket failed\n";
			/* For UDP protocol replace SOCK_STREAM with SOCK_DGRAM */

			//Specify server address for client to connect to server.
			memset(&sa_in, 0, sizeof(sa_in));
			memcpy(&sa_in.sin_addr, rp->h_addr, rp->h_length);
			sa_in.sin_family = rp->h_addrtype;
			sa_in.sin_port = htons(port);

			//Display the host machine internet address

			//std::cout << "Connecting to remote host:";
			std::cout << inet_ntoa(sa_in.sin_addr) << endl;


			//Connect Client to the server
			if (connect(s, (LPSOCKADDR)&sa_in, sizeof(sa_in)) == SOCKET_ERROR)
				throw "connect failed\n";


			cout << "Type operation(get, put, list or delete), enter \"quit\" to leave:";
			string operation;
			cin >> operation;

			string userName = getSystemUserName();
			if (operation == "quit")
			{
				string text = "operation:quit|userName:" + userName;
				const int length = text.length();
				char * title = new char[length + 1];
				title[length] = '\0';
				for (unsigned int i = 0; i < length; i++)
				{
					title[i] = text.at(i);
				}
				string result = sendData(title);
				break;
			}

			string fileName;
			if (operation == "get" || operation == "put" || operation == "delete")
			{
				cout << "Type name of file to be operated:";
				cin >> fileName;
			}
			if (operation == "put")
			{
				if (fileExist(fileName))
				{
					bool binaryFile = isBinaryFile(fileName);
					int binaryFlag = binaryFile ? 1 : 0;
					ifstream fin;
					int count;
					if (binaryFile)
					{
						int fileSize = getFileSize(fileName);
						count = fileSize % BUFFER_SIZE == 0 ? fileSize / BUFFER_SIZE : fileSize / BUFFER_SIZE + 1;
						fin = ifstream(fileName, ifstream::binary);
					}
					else
					{
						count = getTextFileCount(fileName);
						fin = ifstream(fileName);
					}
					string text = "operation:put|userName:" + userName + "|fileName:" + fileName + "|count:" + std::to_string(count) + "|binary:" + std::to_string(binaryFlag);
					const int length = text.length();
					char * title = new char[length + 1];
					title[length] = '\0';
					for (unsigned int i = 0; i < length; i++)
					{
						title[i] = text.at(i);
					}

					sprintf_s(szbuffer, title);
					ibytessent = 0;
					ibufferlen = strlen(szbuffer);
					ibytessent = send(s, szbuffer, ibufferlen, 0);
					if (ibytessent == SOCKET_ERROR)
						throw "Send failed\n";
					/*
					else
					cout << "Message to server: " << szbuffer <<endl;
					*/
					delete title;

					cout << "begin file transfer" << endl;
					if (binaryFile)
					{
						while (count > 0)
						{
							char array[BUFFER_SIZE]; // allocating an array
							memset(array, '\0', BUFFER_SIZE);
							fin.read(array, BUFFER_SIZE);
							ibytessent = 0;
							ibytessent = send(s, array, BUFFER_SIZE, 0);
							if (ibytessent == SOCKET_ERROR)
								throw "Send failed\n";
							/*
							else
							cout << "Message to server: " << szbuffer <<endl;
							*/
							count--;
						}
					}
					else
					{
						while (count > 0)
						{
							char array[BUFFER_SIZE]; // allocating an array
							memset(array, '\0', BUFFER_SIZE);
							int position = 0; //this will be used incremently to fill characters in the array 
							//this loop run until end of file (eof) does not occur
							while (!fin.eof())
							{
								char c = fin.get(); //reading one character from file to array
								if (c != EOF)
								{
									array[position] = c;
									position++;
									if (position == BUFFER_SIZE - 1)	//full
									{
										array[position] = '\0';
										break;
									}
								}
							}
							if (position != BUFFER_SIZE - 1)
							{
								array[position] = '\0';
							}
							ibytessent = 0;
							ibufferlen = strlen(array);
							ibytessent = send(s, array, ibufferlen + 1, 0);
							if (ibytessent == SOCKET_ERROR)
								throw "Send failed\n";
							/*
							else
							cout << "Message to server: " << szbuffer <<endl;
							*/
							count--;
						}
					}
					fin.close();
					cout << "file completely transfered!" << endl;
				}
				else //file could not be opened
				{
					cout << "File could not be opened." << endl;
					continue;
				}
			}
			else if (operation == "get")
			{
				string text = "operation:get|userName:" + userName + "|fileName:" + fileName;
				const int length = text.length();
				char * title = new char[length + 1];
				title[length] = '\0';
				for (unsigned int i = 0; i < length; i++)
				{
					title[i] = text.at(i);
				}
				string result = sendData(title);
				if (result.find("count") == 0)
				{
					cout << "begin receive file" << endl;
					int count = stringToInt(result.substr(6));
					int index = result.find_first_of("|");
					int binaryFlag = stringToInt(result.substr(index + 8));
					bool isBinaryFile = binaryFlag==1?true:false;
					if (isBinaryFile)
					{
						ofstream ofs(fileName, ofstream::binary);
						while (count > 0)
						{
							memset(szbuffer, '\0', BUFFER_SIZE);
							ibytesrecv = 0;
							if ((ibytesrecv = recv(s, szbuffer, BUFFER_SIZE, 0)) == SOCKET_ERROR)
								throw "Receive failed\n";
							ofs.write(szbuffer, BUFFER_SIZE);
							ofs.flush();
							count--;
						}
						cout << "file completely received!" << endl;
						ofs.close();
					}
					else
					{
						ofstream ofs(fileName, ofstream::out);
						while (count > 0)
						{
							memset(szbuffer, '\0', BUFFER_SIZE);
							ibytesrecv = 0;
							if ((ibytesrecv = recv(s, szbuffer, BUFFER_SIZE, 0)) == SOCKET_ERROR)
								throw "Receive failed\n";
							szbuffer[BUFFER_SIZE - 1] = '\0';
							ofs << szbuffer;
							ofs.flush();
							count--;
						}
						cout << "file completely received!" << endl;
						ofs.close();
					}
				}
				else
				{
					cout << result << endl;
				}
			}
			else if (operation == "list")
			{
				string text = "operation:list|userName:" + userName;
				const int length = text.length();
				char * title = new char[length + 1];
				title[length] = '\0';
				for (unsigned int i = 0; i < length; i++)
				{
					title[i] = text.at(i);
				}
				string result = sendData(title);
				if (result.find("count") == 0)
				{
					int count = stringToInt(result.substr(6));
					string fileList;
					while (count > 0)
					{
						memset(szbuffer, '\0', BUFFER_SIZE);
						ibytesrecv = 0;
						if ((ibytesrecv = recv(s, szbuffer, BUFFER_SIZE, 0)) == SOCKET_ERROR)
							throw "Receive failed\n";
						szbuffer[BUFFER_SIZE - 1] = '\0';
						fileList += szbuffer;
						count--;
					}
					cout << "files in directory in server:" << endl;
					cout << fileList << endl;
				}
			}
			else if (operation == "delete")
			{
				string text = "operation:delete|userName:" + userName + "|fileName:" + fileName;
				const int length = text.length();
				char * title = new char[length + 1];
				title[length] = '\0';
				for (unsigned int i = 0; i < length; i++)
				{
					title[i] = text.at(i);
				}
				string result = sendData(title);
				cout << result << endl;
			}
			else
			{
				string text = "operation:error|userName:" + userName;
				const int length = text.length();
				char * title = new char[length + 1];
				title[length] = '\0';
				for (unsigned int i = 0; i < length; i++)
				{
					title[i] = text.at(i);
				}
				string result = sendData(title);

				cout << "operation error!" << endl;
			}
		}
	} // try loop

	//Display any needed error response.

	catch (char *str) { cerr << str << ":" << dec << WSAGetLastError() << endl; }

	//close the client socket
	closesocket(s);

	/* When done uninstall winsock.dll (WSACleanup()) and exit */
	WSACleanup();
	return 0;
}







